#ifndef __M_DB_H__
#define __M_DB_H__
#include "util.hpp"
#include <mutex>
#include <cassert>

class user_table{
    private:
        MYSQL* _mysql; // mysql操作句柄
        std::mutex _mutex; // 互斥锁 保护数据库的访问操作

    public:
        user_table(const std::string& host,
                const std::string& username,
                const std::string& password,
                const std::string& dbname,
                uint16_t port = 3306){
                    _mysql = mysql_util::mysql_create(host,username,password,dbname,port);
                    assert(_mysql != NULL);
        }
        ~user_table(){
            mysql_util::mysql_destroy(_mysql);
            _mysql = NULL;
        }

        // 注册时 新增用户
        bool insert(Json::Value& user){
#define INSERT_USER "insert user values(null,'%s','%s',1000,0,0);"

            // 验证是否账号密码都有
            if(user["username"].isNull() || user["password"].isNull()){
                DLOG("input password or username");
                return false;
            }

            // sprintf(void* buf,char* fomat,...) 格式化为format之后 放到buf
            char sql[4096] = {0};
            sprintf(sql,INSERT_USER,user["username"].asCString(),user["password"].asCString());
            bool ret = mysql_util::mysql_exec(_mysql,sql);
            if(ret == false){
                DLOG("insert user failed!");
                return false;
            }            
            return true;
        }

        // 登录验证 并且返回详细的用户信息
        bool login(Json::Value& user){
            // 用户名和密码共同作为查询过滤条件，查询到数据则表示用户名密码一致，没有信息则用户名或者密码错误
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password='%s';"
            char sql[4096] = {0};
            sprintf(sql,LOGIN_USER,user["username"].asCString(),user["password"].asCString());
            MYSQL_RES* res = NULL;

            {
                // <类型> (指向对象)
                // 这里用智能指针的方式使用锁 则作用域中会创建锁 脱离此作用域则会自动调用析构释放锁 不用自己手动释放 
                std::unique_lock<std::mutex> loce(_mutex);
                bool ret = mysql_util::mysql_exec(_mysql,sql);
                if(ret == false){
                    DLOG("user login failed!");
                    return false;
                }

                // 要么有数据 要么没有数据 有数据则只有一条数据
                res = mysql_store_result(_mysql);
                if(res == NULL){
                    DLOG("no user info!");
                    return false;
                }
            }

            int row_num = mysql_num_rows(res);
            if(row_num != 1){
                DLOG("user info not unique!");
                return false;
            }
            MYSQL_ROW row = mysql_fetch_row(res); // 拿到该行的内容
            // 获取用查询语句查询到的四个数据
            user["id"] = (Json::UInt64)std::stol(row[0]);
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = std::stoi(row[2]);
            user["win_count"] = std::stoi(row[3]);
            
            mysql_free_result(res);
            return true;
        }

        // 通过用户名获取用户信息
        bool select_by_name(const std::string& name, Json::Value& user){
#define USER_BY_NAME "select id, score, total_count, win_count from user where username='%s';"   
            char sql[4096] = {0};
            sprintf(sql,USER_BY_NAME,name.c_str());
            MYSQL_RES* res = NULL;

            {
                // 这里也要获取用户信息 所以也加锁
                std::unique_lock<std::mutex> lock(_mutex);
                bool ret = mysql_util::mysql_exec(_mysql,sql);
                if(ret == false){
                    DLOG("get user by name failed!");
                    return false;
                }

                // 同上 要么有数据 要么没有数据 有数据时只有一条数据
                res = mysql_store_result(_mysql);
                if(res == NULL){
                    DLOG("no user info!");
                    return false;
                }
            }

            int row_num = mysql_num_rows(res);
            if(row_num != 1){
                DLOG("user info not unique!");
                return false;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            user["id"] = (Json::UInt64)std::stol(row[0]);
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = std::stoi(row[2]);
            user["win_count"] = std::stoi(row[3]);

            mysql_free_result(res);
            return true;
        }

        // 通过用户id获取用户信息
        bool select_by_id(uint64_t id, Json::Value& user){
#define USER_BY_ID "select username, score, total_count, win_count from user where id=%ld;"
            char sql[4096] = {0};
            sprintf(sql,USER_BY_ID,id);
            MYSQL_RES* res = NULL;

            {
                // 要获取用户信息 所以也加锁
                std::unique_lock<std::mutex> lock(_mutex);
                bool ret = mysql_util::mysql_exec(_mysql,sql);
                if(ret == false){
                    DLOG("get user by id failed!");
                    return false;
                }

                res = mysql_store_result(_mysql);
                if(res == NULL){
                    DLOG("no user info!");
                    return false;
                }
            }

            int row_num = mysql_num_rows(res);
            if(row_num != 1){
                DLOG("user info not unique!");
                return false;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            user["id"] = (Json::UInt64)id;
            user["username"] = row[0];
            user["score"] = (Json::UInt64)std::stol(row[1]);
            user["total_count"] = std::stoi(row[2]);
            user["win_count"] = std::stoi(row[3]);

            mysql_free_result(res);
            return true;
        }

        // 胜利时天梯分数加10分，战斗场次增加1，胜利场次增加1
        bool win(uint64_t id){
#define USER_WIN "update user set score=score+10, total_count=total_count+1, win_count=win_count+1 where id=%ld;"
            char sql[4096] = {0};
            sprintf(sql,USER_WIN,id);
            bool ret = mysql_util::mysql_exec(_mysql,sql);
            if(ret == false){
                DLOG("update win user info failed!");
                return false;
            }
            return true;
        }

        // 失败时天梯分数减少5，战斗场次增加1，其它不变
        bool lose(uint64_t id){
#define USER_LOSE "update user set score=score-5, total_count=total_count+1 where id=%ld;"
            char sql[4096] = {0};
            sprintf(sql,USER_LOSE,id);
            bool ret = mysql_util::mysql_exec(_mysql,sql);
            if(ret == false){
                DLOG("update lose user info failed!");
                return false;
            }
            return true;
        }
}; 

#endif